package org.cyk.dataes

import org.cyk.dataes.model.AlbumInfoDo
import org.elasticsearch.index.query.QueryBuilders
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder
import org.junit.jupiter.api.Test
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Sort
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder
import javax.annotation.Resource

@SpringBootTest(classes = [DataEsApplication::class])
class TemplateTests {

    @Resource private lateinit var elasticsearchTemplate: ElasticsearchRestTemplate

    /**
     * 全文检索查询(match_all)
     */
    @Test
    fun testMatchAllQuery() {
        val query = NativeSearchQueryBuilder()
            .withQuery(QueryBuilders.matchAllQuery())
            .build()
        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)
        println("总数为: ${hits.totalHits}")
        hits.forEach { println(it.content) }
    }

    /**
     * 全文检索查询(match)
     */
    @Test
    fun testMatchQuery() {
        val query = NativeSearchQueryBuilder()
            .withQuery(QueryBuilders.matchQuery("title", "天气"))
            .build()
        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)
        hits.forEach { println(it.content) }
    }

    /**
     * 精确查询(term)
     */
    @Test
    fun testTerm() {
        val query = NativeSearchQueryBuilder()
            .withQuery(QueryBuilders.termQuery("user_id", 10001))
            .build()
        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)
        hits.forEach { println(it.content) }
    }

    /**
     * 范围查询
     */
    @Test
    fun testRangeQuery() {
        val query = NativeSearchQueryBuilder()
            .withQuery(QueryBuilders.rangeQuery("id").gte(1).lt(4))
            .build()
        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)
        hits.forEach { println(it.content) }
    }

    /**
     * 复合查询(bool)
     */
    @Test
    fun testBoolQuery() {
        val boolQuery = QueryBuilders.boolQuery()
        //必要条件: query.must 得到一个集合
        val mustList = boolQuery.must()
        mustList.add(QueryBuilders.rangeQuery("user_id").gte(10000).lt(10003))
        //其他的搜索条件集合的获取方式类似
        val mustNotList = boolQuery.mustNot()
        val should = boolQuery.should()

        //当然，还有一种简化的写法，如下，下述代码相当于 query.should().add(QueryBuilders.matchAllQuery())
        boolQuery.should(QueryBuilders.matchAllQuery())

        val query =  NativeSearchQueryBuilder()
            .withQuery(boolQuery)
            .build()
        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)
        hits.forEach { println(it.content) }
    }

    /**
     * 排序和分页
     */
    @Test
    fun testSortAndPage() {
        val query = NativeSearchQueryBuilder()
            .withQuery(QueryBuilders.matchAllQuery())
            .withPageable(
                PageRequest.of(0, 3) //从下标 0 开始，向后查询 3 条数据
                    .withSort(Sort.by(Sort.Order.desc("id"))) //根据 id 降序排序(这里也可以根据多个字段进行升序降序)
            ).build()
        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)
        hits.forEach{ println(it.content) }
    }

    /**
     * 高亮搜索
     */
    @Test
    fun testHighLight() {
        //定义高亮字段
        val field = HighlightBuilder.Field("title")
        //a) 前缀标签
        field.preTags("<span style='color:red'>")
        //b) 后缀标签
        field.postTags("</span>")
        //c) 高亮的片段长度(多少个几个字需要高亮)
        field.fragmentSize(2)
        //d) 每个字段高亮片段的数量，例如 1 表示获取每个字段的一个高亮片段
        field.numOfFragments(1)

        // withHighlightFields(Field... 高亮字段数组)
        val query = NativeSearchQueryBuilder()
            .withQuery(QueryBuilders.matchQuery("title", "天气"))
            .withHighlightFields(field)
            .build()
        val hits = elasticsearchTemplate.search(query, AlbumInfoDo::class.java)

        //注意，hit.content 中本身是没有高亮数据的，因此这里需要手工处理
        hits.forEach {
            val result = it.content
            //根据高亮字段名称，获取高亮数据集合，结果是 List<String>
            val hList = it.getHighlightField("title")
            if(hList.size > 0) {
                //有高亮数据
                result.title = hList.get(0)
            }
            println(result)
        }
    }

}